package network.TCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;
    
    Map<String,String> map = new HashMap<>();

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        map.put("cat","小猫");
    }

    public void start() throws IOException {
        System.out.println("服务器启动");

        // 线程池优化下面直接创建 Thread 代码
        ExecutorService service = Executors.newCachedThreadPool();

        while (true) {
            // 通过 accept方法，将内核中建立好地连接拿到应用程序中
            Socket clientSocket = serverSocket.accept();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            // 创建线程，可以同时几个客户端同时使用
            // Thread t = new Thread(() -> {
            // try {
            //       processConnection(clientSocket);
            //     } catch (IOException e) {
            //       throw new RuntimeException(e);
            //       }
            //     });
            // t.start();
        }
    }

    // 通过这个方法来处理当前的连接
    private void processConnection(Socket clientSocket) throws IOException {
        // 先打印日志，表示有客户端连上了
        System.out.printf("%s %d 客户端上线", clientSocket.getInetAddress(), clientSocket.getPort());
        System.out.println();

        // 进行数据交互
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 使用 try() 的方式，防止忘记关闭流对象(没有关闭 clientSocket 对象)
            while (true) {
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    System.out.printf("%s %d 客户端下线", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                // 1. 开始读取并且解析，这里用到 next（遇到空白符就返回）
                String request = scanner.next();
                // 2. 根据请求，做出响应
                String response = process(request);
                // 3. 把相应写回到客服端
                //    可以使用 String 转成字符数组，写入到 OutputStream
                //    也可以使用 PrintWriter 把 OutputStream 包裹一下,来写入字符串
                PrintWriter printWriter = new PrintWriter(outputStream);
                //    不是写到控制台，而是 OutputStream 对应的流对象，也就是 clientSocket 中，也就通过网络发给连接的另外一端了
                printWriter.println(response);
                //     还得刷新缓冲区
                printWriter.flush();
                // 4. 打印一下这次交互的内容
                System.out.printf("%s %d req = %s, resp = %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            clientSocket.close();
        }

    }

    private String process(String request) {
        return map.getOrDefault(request, " 没有这个词");
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
